﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using dotNetMembership = System.Web.Security.Membership;
using dotNetRoles = System.Web.Security.Roles;
using Binding = System.Web.Mvc.BindingHelperExtensions;
using StarterKits.Mvc.Filters.FormsAuthentication;
using StarterKits.Mvc.Membership.Controllers;
using P.InOut.NetMvc.Logging;
using P.InOut.NetMvc.Models;
using P.InOut.NetMvc.Properties;

namespace P.InOut.NetMvc.Controllers
{
    public class FormsAuthenticationAdministrationController : StarterKits.Mvc.Membership.Controllers.BaseFormsAuthenticationAdministrationController
    {
        private InOutDatabaseLogger logger = new InOutDatabaseLogger();

		[RequiresRole( Role="Administrator" )]
        public ActionResult Index()
        {
			ViewData["RolesEnabled"] = dotNetRoles.Enabled;
			return View();
        }

        #region UserDetails Override

        /// <summary>
        /// Override User Details to add Profile and Team data
        /// </summary>
        /// <param name="id">Guid Member Id</param>
        /// <returns>Action Result</returns>
        public override ActionResult UserDetails(Guid id)
        {
            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {
                MembershipUser user = dotNetMembership.GetUser(id);
                InOutMembershipUserAndRolesViewData data = new InOutMembershipUserAndRolesViewData
                {
                    Profile = db.Profiles.Single<Profile>(p => p.UserId == (Guid)user.ProviderUserKey),
                    AllTeams = db.Teams.ToList<Team>(),
                    UsersTeams = db.MemberTeams.Where(m => m.UserId == (Guid)user.ProviderUserKey).Select(m => m.Team).ToList<Team>(),
                    RolesEnabled = dotNetRoles.Enabled,
                    RequiresQuestionAndAnswer = dotNetMembership.RequiresQuestionAndAnswer,
                    User = user,
                    AllRoles = dotNetRoles.GetAllRoles().OrderBy(x => x).ToList(),
                    UsersRoles = dotNetRoles.GetRolesForUser(user.UserName).OrderBy(x => x).ToList()
                };

                return View("DisplayUser", data);
            }
        }

        #endregion

        #region Extended Membership Methods

        /// <summary>
        /// Method used to delete a user
        /// </summary>
        /// <param name="id">Guid Member Id</param>
        /// <returns>Action Result</returns>
        public ActionResult DeleteUser(Guid id)
        {
            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {
                // Grab all existing data that is tied to the user and delete
                List<MemberTeam> teams = db.MemberTeams.Where(mt => mt.UserId == id).ToList<MemberTeam>();
                List<Update> updates = db.Updates.Where(u => u.UserId == id).ToList<Update>();
                Profile profile = db.Profiles.Single(p => p.UserId == id);

                db.MemberTeams.DeleteAllOnSubmit(teams);
                db.Updates.DeleteAllOnSubmit(updates);
                db.Profiles.DeleteOnSubmit(profile);

                db.SubmitChanges();

                // Delete user
                MembershipUser user = dotNetMembership.GetUser(id);
                dotNetMembership.DeleteUser(user.UserName);

                return RedirectToAction("Users", "FormsAuthenticationAdministration");
            }
        }

        #endregion

        #region Profile Management

        [NonAction]
        public virtual void OnBeforeUpdateProfile(string adminName, string userName) { }

        [NonAction]
        public virtual void OnAfterUpdateProfile(string adminName, string userName)
        {
            // Log User Added to Role
            string message = string.Format("User: {0} Updated User {1}'s Profile", adminName, userName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        /// <summary>
        /// Method for admins to updates a user's profile
        /// </summary>
        /// <returns>Action Result</returns>
        [RequiresRole(Role = "Administrator")]
        public virtual ActionResult UpdateUserProfile()
        {
            MembershipUser admin = dotNetMembership.GetUser();

            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {
                // Get username from hidden field
                string userName = Request.Form["UserName"];

                OnBeforeUpdateProfile(admin.UserName, userName);

                // Get user to use ID in profile query
                MembershipUser user = dotNetMembership.GetUser(userName);
                Profile profile = db.Profiles.Single<Profile>(p => 
                    p.UserId == (Guid)user.ProviderUserKey);

                // Update profile
                Binding.UpdateFrom(profile, Request.Form);

                // Submit update to DB
                db.SubmitChanges();

                OnBeforeUpdateProfile(admin.UserName, userName);

                return RedirectToUserPage(user);
            }

        }

        #endregion

        #region Team Management

        [NonAction]
        public virtual void OnBeforeDeleteTeam(string userName, string teamName) { }

        [NonAction]
        public virtual void OnAfterDeleteTeam(string userName, string teamName)
        {
            // Log User Added to Role
            string message = string.Format("User: {0} Deleted Team: {1}", userName, teamName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        [RequiresRole(Role = "Administrator")]
        public virtual ActionResult DeleteTeam(string teamName)
        {
            MembershipUser admin = dotNetMembership.GetUser();

            using (InOutDataContext db =
                    new InOutDataContext(Settings.Default.dbConn))
            {
                MemberTeam[] teamMembers = (from mt in db.MemberTeams
                                            where mt.Team.Description == teamName
                                            select mt).ToArray<MemberTeam>();

                OnBeforeDeleteTeam(admin.UserName, teamName);

                db.MemberTeams.DeleteAllOnSubmit(teamMembers);
                db.SubmitChanges();

                InOut.NetMvc.Models.Team team = db.Teams.Where(t => t.Description == teamName).Single<Team>();

                db.Teams.DeleteOnSubmit(team);
                db.SubmitChanges();

                OnAfterDeleteTeam(admin.UserName, teamName);

                return RedirectToAction("Teams", "Security");
            }
        }

        [NonAction]
        public virtual void OnBeforeCreateTeam(string userName, string teamName) { }

        [NonAction]
        public virtual void OnAfterCreateTeam(string userName, string teamName)
        {
            // Log User Added to Role
            string message = string.Format("User: {0} Deleted Team: {1}", userName, teamName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public virtual ActionResult CreateTeam(string teamName)
        {
            MembershipUser user = dotNetMembership.GetUser();

            using (InOutDataContext db =
                   new InOutDataContext(Settings.Default.dbConn))
            {
                if (db.Teams.Where(t => t.Description == teamName).Count() == 0)
                {
                    OnBeforeCreateTeam(user.UserName, teamName);

                    P.InOut.NetMvc.Models.Team team = new Team
                    {
                        Description = teamName,
                    };

                    db.Teams.InsertOnSubmit(team);
                    db.SubmitChanges();

                    OnAfterCreateTeam(user.UserName, teamName);
                }

                return RedirectToAction("Teams", "Security");
            }
        }

        [RequiresRole(Role = "Administrator")]
        public virtual ActionResult Teams()
        {
            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {
                string[] teams = db.Teams.Select(t => t.Description).ToArray<string>();
                return View(teams.OrderBy(x => x).ToList());
            }

        }

        [RequiresRole(Role = "Administrator")]
        public virtual ActionResult Team(string teamName)
        {
            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {
                var team = new MembershipTeamData();

                team.Name = teamName;

                var aspMembers = (from m in db.MemberTeams
                                  where m.Team.Description == teamName
                                  orderby m.Aspnet_Membership.Aspnet_User.UserName
                                  select m.Aspnet_Membership).ToList<Aspnet_Membership>();

                team.Users = new List<MembershipUser>();
                foreach (Aspnet_Membership m in aspMembers)
                {
                    MembershipUser user = dotNetMembership.GetUser(m.UserId);
                    team.Users.Add(user);
                }

                return View("UsersInTeam", team);
            }

        } 

        [NonAction]
        public virtual void OnBeforeAddUserToTeam(string userName, string teamName) { }

        [NonAction]
        public virtual void OnAfterAddUserToTeam(string userName, string teamName)
        {
            // Log User Added to Role
            string message = string.Format("User: {0} Added To Team: {1}", userName, teamName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="teamId"></param>
        /// <param name="add"></param>
        /// <returns></returns>
        [RequiresRole(Role = "Administrator")]
        public virtual ActionResult AddUserToTeam(Guid id, string teamName)
        {
            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {
                Team team = db.Teams.Where(t => t.Description == teamName).Single<Team>();

                // Get user to use ID in profile query
                MembershipUser user = dotNetMembership.GetUser(id);

                MemberTeam memberTeam = new MemberTeam
                {
                    UserId = id,
                    TeamId = team.Id
                };

                OnBeforeAddUserToTeam(user.UserName, teamName);

                db.MemberTeams.InsertOnSubmit(memberTeam);
                db.SubmitChanges();

                OnAfterAddUserToTeam(user.UserName, teamName);

                return RedirectToUserPage(user);
            }
        }

        [NonAction]
        public virtual void OnBeforeRemoveUserFromTeam(string userName, string teamName) { }

        [NonAction]
        public virtual void OnAfterRemoveUserFromTeam(string userName, string teamName)
        {
            // Log User Added to Role
            string message = string.Format("User: {0} Removed From Team: {1}", userName, teamName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        [RequiresRole(Role = "Administrator")]
        public virtual ActionResult RemoveUserFromTeam(Guid id, string teamName)
        {
            using (InOutDataContext db =
                new InOutDataContext(Settings.Default.dbConn))
            {

                // Get user to use ID in profile query
                MembershipUser user = dotNetMembership.GetUser(id);

                MemberTeam memberTeam = 
                    db.MemberTeams.Where(m => m.UserId == id && m.Team.Description == teamName).Single<MemberTeam>();

                OnBeforeRemoveUserFromTeam(user.UserName, teamName);

                db.MemberTeams.DeleteOnSubmit(memberTeam);
                db.SubmitChanges();

                OnAfterRemoveUserFromTeam(user.UserName, teamName);

                return RedirectToUserPage(user);
            }
        }

        #endregion

        #region Logging and Event Overrides

        /// <summary>
        /// Logger Helper Method
        /// </summary>
        /// <param name="message">Log Entry Message</param>
        /// <param name="severity">Log Entry Severity</param>
        /// <param name="userName">Log Entry UserName</param>
        private void LogAction(string message, BitFactory.Logging.LogSeverity severity, string userName)
        {
            // Execute the log entry
            logger.Execute("InOut", "Administration", message, System.DateTime.Now,
                severity, userName);
        }

        public override void OnAfterAddUserToRole(string userName, string roleName)
        {
            // Log User Added to Role
            string message = string.Format("User: {0} Added to Role: {1}", userName, roleName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnAfterCreateRole(string role)
        {
            // Log Role Created
            string message = string.Format("Role: {0} Added", role);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnAfterCreateUser(string userName, string emailAddress, string password, string passwordConfirm, string passwordQuestion, string passwordAnswer)
        {
            // Log User Created
            string message = string.Format("User: {0} Created", userName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);

            try
            {
                System.Web.Security.MembershipUser user =
                dotNetMembership.GetUser(userName);

                if (user != null)
                {
                    using (InOutDataContext db =
                        new InOutDataContext(Settings.Default.dbConn))
                    {
                        // Create new Status object for insertion
                        Profile profile = new Profile
                        {
                            DateTimeLastUpdated = DateTime.Now,
                            UserId              = (Guid)user.ProviderUserKey,
                            InStatus            = false,
                            OfficeStatus        = false,
                            Status              = "I'm New"
                        };

                        db.Profiles.InsertOnSubmit(profile);

                        // Create new Update object for insertion
                        Update update = new Update
                        {
                            DateTimeEntered = DateTime.Now,
                            UserId          = (Guid)user.ProviderUserKey,
                            UpdateText      = "Someone just created my account!"
                        };

                        db.Updates.InsertOnSubmit(update);

                        // Insert into db
                        db.SubmitChanges();

                        // Add user to Users group
                        dotNetRoles.AddUserToRole(user.UserName, "User");
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }

        public override void OnAfterDeleteRole(string id)
        {
            // Log Role Deleted
            string message = string.Format("Role ID: {0} Deleted", id);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnAfterPasswordChange(string userName, string currentPassword, string newPassword, string newPasswordConfirm)
        {
            // Log Password Changed
            string message = string.Format("User: {0}'s Password Has Changed", userName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }


        public override void OnAfterRemoveUserFromRole(string userName, string roleName)
        {
            // Log User Removed From Role
            string message = string.Format("User: {0} Was Removed From Role: {1}", 
                userName, roleName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnAfterResetPassword(string email, string userName, string newPassword)
        {
            // Log Password Reset
            string message = string.Format("User: {0}'s Password Was Reset", userName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnAfterUnlockUser(string id)
        {
            // Log User Onblocked
            string message = string.Format("User ID: {0}'s Account Was Unlocked", id);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnAfterUpdateUser(string userName)
        {
            // Log User Updated
            string message = string.Format("User: {0} Was Updated", userName);
            LogAction(message, BitFactory.Logging.LogSeverity.Info, User.Identity.Name);
        }

        public override void OnCreateUserError(string userName, string emailAddress, string password, string passwordConfirm, string passwordQuestion, string passwordAnswer, string error)
        {
            // Log Create User Error
            string message = string.Format("There was an Error: {0} creating User: {1}", 
                error, userName);
            LogAction(message, BitFactory.Logging.LogSeverity.Error, User.Identity.Name);
        }

        #endregion
    }

    #region ViewData Models

    public class MembershipTeamData
    {
        public string Name { get; set; }

        public List<MembershipUser> Users { get; set; }
    }

    public class UserProfileData
    {
        public MembershipUser User { get; set; }

        public Profile Profile { get; set; }
    }

    #endregion
}